﻿namespace FallingRocks
{
    using System;
    using System.Collections.Generic;
    using System.Threading;

    public class RealEngine
    {
        private readonly IRenderer renderer;
        private readonly IUserInterface userInterface;
        private List<IRenderable> gameObjects;
        private Dwarf player;
        private int sleepTime;

        public RealEngine(IRenderer renderer, IUserInterface userInterface, int sleepTime = 10)
        {
            this.renderer = renderer;
            this.userInterface = userInterface;
            this.gameObjects = new List<IRenderable>();
            try
            {
                this.SetSleepTime(sleepTime);
            }
            catch (ArgumentOutOfRangeException exception)
            {
                throw new InputException<int>(sleepTime, exception.Message, exception);
            }            
        }

        public virtual void MovePlayerLeft()
        {
            this.player.MoveLeft();
        }

        public virtual void MovePlayerRight(int end)
        {
            this.player.MoveRight(end);
        }
                
        public virtual void Start(Thread thread)
        {
            IFactory factory = new RocksFactory();
            int difficulty = 5;
            int lives;
            int score;

            if (!(this.player == null))
            {
                lives = this.player.Lives;
                score = this.player.Score;
            }
            else
            {
                lives = 3;
                score = 0;
            }

            this.IncludeObject(new Dwarf(new Position(Console.WindowWidth / 2 - 2, Console.WindowHeight - 1), new char[] { ' ', '/', '0', '\\', ' ' }, default(Color), lives, score));
            int counter = 1;
            Console.Clear();
            this.gameObjects = new List<IRenderable>();

            while (true)
            {              
                if (counter == 10)
                {
                    foreach (GameObject gameObject in factory.CreateObjects(difficulty))
                    {
                        this.IncludeObject(gameObject);
                    }                   

                    foreach (GameObject gameObject in this.gameObjects)
                    {
                        gameObject.Update();
                    }

                    this.player.Update();
                    this.renderer.RenderObjects(this.gameObjects);

                    counter = 1;
                }

                this.userInterface.ProcessInput();

                this.renderer.RenderObjects(new List<IRenderable>() { this.player });
                                
                if (CollisionDetector.CheckForCollisions(this.gameObjects, this.player))
                {
                    thread.Abort();
                    break;
                }
                
                this.gameObjects.RemoveAll(gameObject => gameObject.Position.Y == this.player.Position.Y);

                counter++;

                System.Threading.Thread.Sleep(this.sleepTime);
            }

            if (this.player.Lives > 0)
            {
                this.renderer.RenderObjects(new List<IRenderable>() { new MenuItem(new Position(Console.WindowWidth / 2 - 10, Console.WindowHeight / 2), string.Concat("You have " + this.player.Lives + " lives left!").ToCharArray(), Color.Cyan) });
                thread = new Thread(new ThreadStart(new Sound().MissionImpossibleTune));
                Console.ReadKey();
                thread.Start();
                this.Start(thread);
            }
            else
            {
                this.renderer.RenderObjects(new List<IRenderable>() { new MenuItem(new Position(Console.WindowWidth / 2 - 10, Console.WindowHeight / 2), string.Concat("Game over! Your score is " + this.player.Score).ToCharArray(), Color.Cyan) });
                thread = new Thread(new ThreadStart(new Sound().CopBererlyHills));
                thread.Start();
                Console.ReadKey();
            }

            return;
        }

        public void Pause()
        {
            throw new NotImplementedException();
        }

        private void IncludeObject(GameObject objectToInclude)
        {
            if (objectToInclude is Dwarf)
            {
                this.IncludePlayer(objectToInclude as Dwarf);
            }
            
            this.gameObjects.Add(objectToInclude);
        }

        private void IncludePlayer(GameObject gameObject)
        {
            this.RemovePlayer(this.gameObjects);
            this.player = gameObject as Dwarf;
        }

        private void RemovePlayer(IList<IRenderable> collection)
        {
            foreach (IRenderable renderable in collection)
            {
                if (renderable is Dwarf)
                {
                    collection.Remove(renderable);
                    break;
                }
            }
        }

        private void SetSleepTime(int value)
        {
            if (value < 0)
            {
                throw new ArgumentOutOfRangeException("Sleep time cannot be negative.");
            }

            this.sleepTime = value;            
        }
    }
}
